﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using YawetagLib.Data;
using YawetagLib.Security;

namespace YawetagLib.Services
{
    public abstract class BaseService : IService
    {
        private static readonly object _lock = new object();

        static BaseService()
        {
            GenericMethodInfo = new Dictionary<Type, MethodInfo>();
        }

        protected BaseService(IServiceFactory serviceFactory, IDataManager dataManager)
        {
            ServiceFactory = serviceFactory;
            DataManager = dataManager;

            InitializeAtHelperMethodInfo();
        }

        private void InitializeAtHelperMethodInfo()
        {
            DataManagerGetInfo = DataManager.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Public)
                .Where(i => i.IsGenericMethod && i.Name == "Get" && i.GetParameters().Any())
                .Single();

            ServiceFactoryGetInfo = ServiceFactory.GetType()
                .GetMethods(BindingFlags.Instance | BindingFlags.Public)
                .Where(i => i.IsGenericMethod && i.Name == "Get")
                .Single();
        }

        private static MethodInfo GetGenericMethodInfo(MethodInfo methodInfo, Type type)
        {
            lock (_lock)
            {
                if (!GenericMethodInfo.ContainsKey(type))
                {
                    GenericMethodInfo.Add(type, methodInfo.MakeGenericMethod(type));
                }
            }

            return GenericMethodInfo[type];
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual T At<T>(Guid dataContextIdentifier) where T : class
        {
            Type type = typeof(T);

            if (typeof(IRepository).IsAssignableFrom(type))
            {
                return (T)GetGenericMethodInfo(DataManagerGetInfo, type).Invoke(DataManager, new object[] { dataContextIdentifier });
            }
            if (typeof(IService).IsAssignableFrom(type))
            {
                return (T)GetGenericMethodInfo(ServiceFactoryGetInfo, type).Invoke(ServiceFactory, null);
            }

            throw new Exception("At Helper's generic parameter must implement either IRepository or IService interface");
        }

        protected virtual T At<T>() where T : class
        {
            return At<T>(Guid.Empty);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!IsDisposed)
            {
                if (disposing)
                {
                    ManagedResourceCleanup();
                }
                UnmanagedResourceCleanup();
                IsDisposed = true;
            }
        }

        protected virtual void ManagedResourceCleanup()
        {
        }

        protected virtual void UnmanagedResourceCleanup()
        {
        }

        ~BaseService()
        {
            Dispose(false);
        }

        protected UserContext UserContext
        {
            get { return Thread.CurrentPrincipal as UserContext; }
        }

        protected IDataManager DataManager { get; private set; }
        protected IServiceFactory ServiceFactory { get; private set; }

        private MethodInfo DataManagerGetInfo { get; set; }
        private MethodInfo ServiceFactoryGetInfo { get; set; }
        private static IDictionary<Type, MethodInfo> GenericMethodInfo { get; set; }

        private bool IsDisposed { get; set; }
    }
}
